﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MineFlagger
{
    public partial class DigitsControl : Control
    {

        List<int> digits;             // list of displayed digits
        Image digitsBMP;              // Array of digits 0-9 to blit from


        const int digitWidth = 13;    // size of digits in pixles
        const int digitHeight = 23;

        int left = 0;                 // digit drawing offsets
        int top = 0;

        int borderThickness = 1;

        int borderWidth;              // dynamicly calculated by invalidateBorders()
        int borderHeight;
        int bottom;
        int right;

        
        public DigitsControl()
        {
            InitializeComponent();

            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);

            loadDigitsBMP();
            digits = new List<int>(10);

            // defaults
            MinDigits = 3;
            MaxDigits = 5;
        }


        #region Public Properties: Value, MaxDigits, MinDigits

        int _value;
        public int Value
        {
            get { return _value; }
            set
            {
                if (_value == value)
                    return;

                _value = value;
                invalidateDigits();
            }
        }

        int _maxDigits;
        public int MaxDigits
        {
            get { return _maxDigits; }
            set {
                if (_maxDigits == value)
                    return;

                _maxDigits = value;
                invalidateDigits();
            }
        }

        int _minDigits;
        public int MinDigits
        {
            get { return _minDigits; }
            set { 
                if (_minDigits == value)
                    return;
                
                _minDigits = value;
                invalidateDigits();
            }
        }

        #endregion

        #region Digit and Border Calculations

        // breaks Value into base10 digits (includes leading zeros)
        const int NegitiveSignIndex = 11; // index into digits.png
        void invalidateDigits()
        {
            int oldDigitCount = digits != null ? digits.Count : -1;
            

            // make room for negitive sign if neg value
            int maxDigits = Value >= 0 ? MaxDigits : MaxDigits - 1;
            int minDigits = Value >= 0 ? MinDigits : MinDigits - 1;

            int x = Math.Abs(Value);
            digits.Clear();
            while ((x > 0 || digits.Count < minDigits) && digits.Count < maxDigits)
            {
                digits.Add(x % 10);
                x /= 10;
            }

            // add "sign digit" if negitive
            if (Value < 0)
                digits.Add(NegitiveSignIndex);

            if (digits.Count != oldDigitCount)
                invalidateBorders();

            this.Invalidate();
        }

        // recalculates border paramiters based on number of displayed digits
        void invalidateBorders()
        {
            borderWidth = digits.Count * digitWidth + borderThickness * 2;
            borderHeight = digitHeight + borderThickness * 2;
            bottom = top + borderHeight;
            right = left + borderWidth;

            this.Width = left + borderWidth;
            this.Height = top + borderHeight;
        }

        #endregion

        #region Drawing

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);

            // 1. draw border
            Brush upperBorderBrush = Brushes.DarkGray;
            Brush lowerBorderBrush = Brushes.White;

            pe.Graphics.FillRectangle(upperBorderBrush, left, top, borderWidth - borderThickness, borderThickness); // TOP
            pe.Graphics.FillRectangle(upperBorderBrush, left, top, borderThickness, borderHeight - borderThickness); // LEFT

            pe.Graphics.FillRectangle(lowerBorderBrush, right - borderThickness, top + borderThickness, borderThickness, borderHeight - borderThickness); // RIGHT
            pe.Graphics.FillRectangle(lowerBorderBrush, left + borderThickness, bottom - borderThickness, borderWidth - borderThickness, borderThickness); // BOTTOM


            // 2. iterate over digits and draw
            int x = left + borderThickness;
            int y = top + borderThickness;

            for (int i = digits.Count - 1; i >= 0; --i)
            {
                int digit = digits[i];
                pe.Graphics.DrawImage(digitsBMP, x, y, new Rectangle(digitWidth * digit, 0, digitWidth, digitHeight), GraphicsUnit.Pixel);
                x += digitWidth;
            }
        }

        // loads digitsBMP from embeded resource file
        void loadDigitsBMP()
        {
            if (digitsBMP != null) return;

            System.Reflection.Assembly thisExe;
            thisExe = System.Reflection.Assembly.GetExecutingAssembly();
            System.IO.Stream file = thisExe.GetManifestResourceStream("MineFlagger.digits.png");

            digitsBMP = Image.FromStream(file);

            file.Close();

        }

        #endregion




        #region Dynamic segment-based digit drawing (not currently used)

        // note: currently not drawing dynamicly (instead uses 0-9 digits.png) 
        // but we'll keep this around as in theory it could be easaly altered
        // to dynamicly produce letters.
        void drawDigitBMP(int digit, ref Bitmap digitBMP)
        {
            if (digitBMP != null) digitBMP.Dispose();
            digitBMP = new Bitmap(digitWidth, digitHeight);
            Graphics g = Graphics.FromImage(digitBMP);

            if (digitsBMP == null)
                loadDigitsBMP();


            // 1.  draw all 7 segments off
            g.DrawImage(digitsBMP, 0, 0, new Rectangle(digitWidth * 10, 0, digitWidth, digitHeight), GraphicsUnit.Pixel);


            // 2.  draw segments that should be on
            Brush onBrush = Brushes.Red;
            int x, y, height, width;

            //  1111
            // 0    2 
            // 0    2
            //  6666
            // 5    3
            // 5    3
            //  4444


            // 0
            if (digit == 4 || digit == 5 || digit == 6 || digit == 0 || digit == 8 || digit == 9)
            {
                for (x = 1, y = 2, height = 9; x <= 3; ++x, ++y, height -= 2)
                    g.FillRectangle(onBrush, x, y, 1, height);
            }

            // 5
            if (digit == 2 || digit == 6 || digit == 0 || digit == 8)
            {
                for (x = 1, y = 12, height = 9; x <= 3; ++x, ++y, height -= 2)
                    g.FillRectangle(onBrush, x, y, 1, height);
            }

            // 2
            if (digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 0 || digit == 7 || digit == 8 || digit == 9)
            {
                for (x = 11, y = 2, height = 9; x >= 9; --x, ++y, height -= 2)
                    g.FillRectangle(onBrush, x, y, 1, height);
            }

            // 3
            if (digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 7 || digit == 0 || digit == 8 || digit == 9)
            {
                for (x = 11, y = 12, height = 9; x >= 9; --x, ++y, height -= 2)
                    g.FillRectangle(onBrush, x, y, 1, height);
            }

            // 1
            if (digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 || digit == 0 || digit == 8 || digit == 9)
            {
                for (x = 3, y = 1, width = 7; y <= 3; ++x, ++y, width -= 2)
                    g.FillRectangle(onBrush, x, y, width, 1);
            }

            // 4
            if (digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 0 || digit == 8 || digit == 9)
            {
                for (x = 5, y = 19, width = 3; y <= 21; --x, ++y, width += 2)
                    g.FillRectangle(onBrush, x, y, width, 1);
            }

            // 6
            if (digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 4 || digit == 8 || digit == 9)
            {
                x = 4; y = 10; width = 5;
                g.FillRectangle(onBrush, x, y, width, 1); ++y; --x; width += 2;
                g.FillRectangle(onBrush, x, y, width, 1); ++y; ++x; width -= 2;
                g.FillRectangle(onBrush, x, y, width, 1);
            }

            g.Dispose();

        }

        #endregion
    }
}
